﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NEH_SA.Model;

namespace NEH_SA.Utilities
{
    public static class Tools
    {
        /// <summary>
        /// Oblicza CMax wybranej permutacji.
        /// </summary>
        /// <param name="permutacja">Kolejność zadań</param>
        /// <param name="problem">Charakterytyka problemu</param>
        /// <returns></returns>
        public static int ObliczCMax(this List<int> permutacja, Problem problem)
        {
            var wektorMax = Enumerable.Repeat(0, problem.LiczbaMaszyn + 1).ToArray();
            for (int i = 0; i < problem.LiczbaZadań; i++)
            {
                for (int j = 1; j <= problem.LiczbaMaszyn; j++)
                {
                    wektorMax[j] = Math.Max(wektorMax[j] + problem.ZadaniaLista[permutacja[i]][j - 1],
                                            wektorMax[j - 1] + problem.ZadaniaLista[permutacja[i]][j - 1]);
                }
            }
            return wektorMax.Last();
        }

        public static List<T> Move<T>(this List<T> lista, int oldIndex, int newIndex)
        {
            var result = lista.ToList();
            var item = result[oldIndex];

            result.RemoveAt(oldIndex);
            if (newIndex > oldIndex) newIndex--;
            result.Insert(newIndex, item);

            return result;
        }

        public static List<T> Swap<T>(this IEnumerable<T> origin, int a, int b, int c)
        {
            var list = origin.ToList();
            T tmpA = list[a];
            T tmpB = list[b];
            list[a] = tmpB;
            list[b] = list[c];
            list[c] = tmpA;
            return list;
        }

        /// <summary>
        /// Zamienia miejscami dwa elementy listy. Zwraca nową liste.
        /// </summary>
        /// <typeparam name="T">Typ elementu</typeparam>
        /// <param name="origin">Lista oryginalna</param>
        /// <param name="indexA">Index pierwszego elementu</param>
        /// <param name="indexB">Index drugiego elementu</param>
        /// <returns></returns>
        public static List<T> Swap<T>(this IEnumerable<T> origin, int indexA, int indexB)
        {
            var list = origin.ToList();
            T tmp = list[indexA];
            list[indexA] = list[indexB];
            list[indexB] = tmp;
            return list;
        }


        public static List<T> LocalSwap<T>(this List<T> origin, Random rand)
        {
            var list = origin.ToList();
            var indexA = rand.Next(0, origin.Count - 1);
            var indexB = (indexA + new[] { origin.Count - 1, 1 }.Shuffle(rand).First()) % origin.Count;
            T tmp = list[indexA];
            list[indexA] = list[indexB];
            list[indexB] = tmp;
            return list;
        }

        /// <summary>
        /// Generuje losowo początkowe rozwiązanie.
        /// </summary>
        /// <param name="liczbaZadań">Liczba zadań</param>
        /// <param name="random">Klasa Random</param>
        /// <returns></returns>
        public static List<int> GenerujPoczątkowąPermutacje(int liczbaZadań, Random random)
        {
            return Enumerable.Range(0, liczbaZadań).Shuffle(random).ToList();
        }


        /// <summary>
        /// Losowo układa elementy wybranej kolekcji
        /// </summary>
        /// <typeparam name="T">Typ danych</typeparam>
        /// <param name="source">IEnumerable zbiór danych</param>
        /// <param name="rng">Klasa Random</param>
        /// <returns></returns>
        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
        {
            var elements = source.ToArray();
            // Note i > 0 to avoid final pointless iteration
            for (int i = elements.Length - 1; i > 0; i--)
            {
                // Swap element "i" with a random earlier element it (or itself)
                int swapIndex = rng.Next(i + 1);
                yield return elements[swapIndex];
                elements[swapIndex] = elements[i];
                // we don't actually perform the swap, we can forget about the
                // swapped element because we already returned it.
            }

            // there is one item remaining that was not returned - we return it now
            yield return elements[0];
        }

        public static List<int> Move(this List<int> lista, Random rand)
        {
            var a = rand.Next(0, lista.Count - 1);
            int b;
            do
            {
                b = rand.Next(0, lista.Count - 1);
            } while (a == b);

            return lista.Move(a, b);
        }
    }
}
